Presentation: Tweet"If You're Reading .then() It's Too Late"
First there were callbacks, then there were Promises and soon there will be async/await. The JavaScript community has made great strides in wrangling async behavior, but what if I told you that using Promises in your application logic necessarily makes your code harder to test by introducing effects? In this talk I'll demonstrate that including async primitives like callbacks and Promises in Node application logic implies that a side-effect has already occurred and that it's too late for that logic to be tested without any setup. I'll also demonstrate how to use existing libraries and language features to describe effects as pure data, making logic around outside interactions testable without a network, database, or any other environment.
In this talk I'll present a pattern which I have been referring to as "relinquishment of control". By this I mean that instead of injecting functions and objects that cause effects, we can use features and patterns like generators and observables, which allow for intermittent suspension of logic execution, to provide *descriptions* of effects like network requests and database operations to a runtime layer that knows how to execute them and resume logic execution with the result. For example, within a generator function it is possible to yield a descriptive object to the caller of the generator, let it interpret the object as an effect, and push the result or error back onto the generator. It is also possible to return a descriptive object from a scan or fold function in an Observable context like Redux or Cycle.js.
Cycle.js demonstrates that this technique works well with its drivers in a higher-order functional-reactive setting, and at my company, Raise, we've demonstrated that this pattern works successfully both in the client and in a node server in a first-order setting such as with Redux or in an Express request handler. I'll cite the JavaScript modules Cycle.js, redux-saga, and redux-loop (written by me) for client-side examples of this pattern in action, and then walk through a new library Raise is currently developing called composit, which makes this pattern more explicit both in the browser and in Node.
My hope is that folks in the audience who do use JavaScript will come away at least being able to recognize when async behavior affects the testability and reliability of their application code. Even better, they may find that existing tools are usable within their current stack, and my best hope is that folks will be able to take the concepts that I present, learn from the examples, and apply them to their own open source libraries and contributions.
Just a note, this talk and much of the code it is about was inspired not only by the libraries I mentioned, but also in part by a talk by Richard Feldman which can be found at https://www.youtube.com/watch?v=6EdXaWfoslc, and I do have his permission to reference it.
Download slides